﻿using System;
using System.Collections.Generic;
using System.Linq;
using Algoritmia.Utils;

namespace Algoritmia.DataStructures.Maps
{
    public class LinkedListMap<TKey,TValue> : IMap<TKey,TValue>
        where TKey : IEquatable<TKey>
    {
        private readonly LinkedList<KeyValuePair<TKey, TValue>> _map;
        private readonly ItemsView<TKey, TValue> _itemsView;
        private readonly KeysView<TKey, TValue> _keysView;
        private readonly ValuesView<TKey, TValue> _valuesView;
        private readonly EqualityComparison<TKey> _equalityComparison;

        public LinkedListMap(IEnumerable<KeyValuePair<TKey, TValue>> items = null, EqualityComparison<TKey> keysEqualityComparison = null)
        {
            _map = new LinkedList<KeyValuePair<TKey, TValue>>(items ?? Enumerable.Empty<KeyValuePair<TKey, TValue>>());
            _equalityComparison = AlgoritmiaFactory.GetDefaultEqualityComparison(keysEqualityComparison);
            _itemsView = new ItemsView<TKey, TValue>(this);
            _keysView = new KeysView<TKey, TValue>(this);
            _valuesView = new ValuesView<TKey, TValue>(this);
        }

        #region IMap<TKey,TValue> Members

        public TValue this[TKey key]
        {
            get
            {
                foreach(var kv in _map)
                {
                    if (_equalityComparison(kv.Key,key))
                    {
                        return kv.Value;
                    }
                }
                throw new KeyNotFoundException(key.ToString());
            }
            set { 
                Remove(key);
                Add(key,value);
            }
        }

        public void Add(TKey key, TValue value)
        {
            _map.AddLast(new KeyValuePair<TKey, TValue>(key, value));
        }

        public bool Contains(TKey key)
        {
            return _map.Any( kv => _equalityComparison(kv.Key,key));
        }

        public TValue Get(TKey key, TValue defaultValue)
        {
            TValue retValue;
            try
            {
                retValue = this[key];
            }
            catch (KeyNotFoundException)
            {
                retValue = defaultValue;
            }
            return retValue;
        }

        public ICollectionView<TKey> Keys()
        {
            return _keysView;
        }

        public ICollectionView<KeyValuePair<TKey, TValue>> Items()
        {
            return _itemsView;
        }

        public bool Remove(TKey key)
        {
            foreach (var kv in _map)
            {
                if (_equalityComparison(kv.Key,key))
                {
                    _map.Remove(kv);
                    return true;
                }
            }
            return false;
        }

        public TValue SetDefault(TKey key, TValue defaultValue)
        {
            TValue retValue;
            try
            {
                retValue = this[key];
            }
            catch (KeyNotFoundException)
            {
                retValue = defaultValue;
                _map.AddLast(new KeyValuePair<TKey, TValue>(key, defaultValue));
            }
            return retValue;
        }

        public ICollectionView<TValue> Values()
        {
            return _valuesView;
        }

        #endregion

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _map.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region ICountable Members

        public int Count
        {
            get { return _map.Count; }
        }

        #endregion

    }
}
